package timing.ukulele.storage.service;

import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import timing.ukulele.storage.entity.NodeEntity;
import timing.ukulele.storage.event.PreviewEventFactory;
import timing.ukulele.storage.event.PreviewMessage;
import timing.ukulele.storage.service.minio.ObjectService;
import timing.ukulele.storage.util.PDFStreamUtil;
import timing.ukulele.storage.util.PdfConvertUtils;
import timing.ukulele.storage.util.SvgUtils;
import timing.ukulele.storage.util.UpdateUtil;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.concurrent.Executors;

@Service
@Slf4j
public class PreviewService {


    private final ObjectService objectService;
    private final PdfConvertUtils convertUtil;
    private final MongoOperations mongoOperations;
    private final RingBuffer<PreviewMessage> ringBuffer;

    public PreviewService(ObjectService objectService, PdfConvertUtils convertUtil, MongoOperations mongoOperations) {
        this.objectService = objectService;
        this.convertUtil = convertUtil;
        this.mongoOperations = mongoOperations;
        this.ringBuffer = initRingBuffer();
    }

    private RingBuffer<PreviewMessage> initRingBuffer() {
        //指定事件工厂
        PreviewEventFactory factory = new PreviewEventFactory();
        //指定ringbuffer字节大小，必须为2的N次方（能将求模运算转为位运算提高效率），否则将影响效率
        int bufferSize = 1024 * 256;
        //单线程模式，获取额外的性能
        Disruptor<PreviewMessage> disruptor = new Disruptor<>(factory, bufferSize, Executors.defaultThreadFactory(),
                ProducerType.SINGLE, new BlockingWaitStrategy());
        //设置事件业务处理器---消费者
//        disruptor.handleEventsWithWorkerPool(this);
        disruptor.handleEventsWith((event, sequences, endOfBatch) -> {
            log.error("消费者正在消费预览消息：{}", event);
            if (event != null && event.getNode() != null)
                getPreviewInputStream(event.getNode());
        });
        // 启动disruptor线程
        disruptor.start();
        //获取ringbuffer环，用于接取生产者生产的事件
        return disruptor.getRingBuffer();
    }

    public void publishPreviewEvent(NodeEntity node) {
        this.ringBuffer.tryPublishEvent((event, sequence) -> {
            event.setNode(node);
            log.error("往消息队列中添加预览消息：{}", node);
        });
    }

    public static void main(String[] args) {
        String filePath = "a.txt"; // 要判断的文件路径
        String contentType = MediaTypeFactory.getMediaType(filePath).orElse(MediaType.APPLICATION_OCTET_STREAM).toString();
        // pdf--------->application/pdf
        // ppt--------->application/vnd.ms-powerpoint
        // pptx--------->application/vnd.openxmlformats-officedocument.presentationml.presentation
        // doc---------->application/msword
        // docx--------->application/vnd.openxmlformats-officedocument.wordprocessingml.document
        // xls---------->application/vnd.ms-excel
        // xlsx--------->application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
        // mp3-------------->audio/mpeg
        // mp4-------------->video/mp4
        System.out.println("文件类型为：" + contentType);
    }

    public void previewFile(NodeEntity node, HttpServletResponse response) {
        try (ServletOutputStream out = response.getOutputStream()) {
            InputStream inputStream = getPreviewInputStream(node);
            if (inputStream != null) {
                String contentType = MediaTypeFactory.getMediaType(node.getPreviewPath()).orElse(MediaType.APPLICATION_OCTET_STREAM).toString();
                response.setContentType(contentType);
                if (contentType.contains("application/pdf")) {
                    String name = node.getName();
                    response.setHeader("Content-disposition",
                            String.format("attachment; filename=\"%s\"", new String(name.getBytes(StandardCharsets.UTF_8), "ISO8859-1")));
                }
                IOUtils.copy(inputStream, out);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // pdf--------->application/pdf
    // ppt--------->application/vnd.ms-powerpoint
    // pptx--------->application/vnd.openxmlformats-officedocument.presentationml.presentation
    // doc---------->application/msword
    // docx--------->application/vnd.openxmlformats-officedocument.wordprocessingml.document
    // xls---------->application/vnd.ms-excel
    // xlsx--------->application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
    // mp3-------------->audio/mpeg
    // mp4-------------->video/mp4
    // png-------------->image/png
    // txt-------------->text/plain
    private InputStream getPreviewInputStream(NodeEntity node) {
        InputStream inputStream = null;
        try {
            if (StringUtils.hasText(node.getPreviewPath())) {
                inputStream = objectService.getObject(node.getOwner(), node.getPreviewPath());
            }
            if (inputStream == null || !StringUtils.hasText(node.getPreviewPath())) {
                String contentType = MediaTypeFactory.getMediaType(node.getName()).orElse(MediaType.APPLICATION_OCTET_STREAM).toString();
                if (contentType.startsWith("video")
                        || contentType.startsWith("audio")
                        || contentType.startsWith("image")
                        || contentType.contains("application/pdf")
                        || contentType.contains("text/plain")) { // 不需要转换
                    node.setPreviewPath(node.getPath());
                    inputStream = objectService.getObject(node.getOwner(), node.getPath());
                } else if (contentType.contains("office")
                        || contentType.contains("powerpoint")
                        || contentType.contains("word")
                        || contentType.contains("excel")) { // 转成pdf
                    // 获取原始文件
                    InputStream object = objectService.getObject(node.getOwner(), node.getPath());
                    // 调用libreoffice转成pdf
                    File convertedFile = convertUtil.toPdf(node.getPath(), object);
                    // pdf文件转成输入流
                    FileInputStream fileInputStream = new FileInputStream(convertedFile);
                    String previewPath = node.getPreviewPath();
                    if (!StringUtils.hasText(previewPath)) {
                        previewPath = UUID.randomUUID() + ".pdf";
                    }
                    objectService.putObject(node.getOwner(), previewPath, fileInputStream, "application/pdf");
                    node.setPreviewPath(previewPath);
                }
                // 无法转换的就是不支持预览的文件，如zip等
                if (StringUtils.hasText(node.getPreviewPath())) {
                    NodeEntity entity = new NodeEntity();
                    entity.setId(node.getId());
                    entity.setPreviewPath(node.getPreviewPath());
                    entity.setParentId(null);
                    entity.setSecret(null);
                    entity.setUserAuthority(null);
                    entity.setGroupAuthority(null);
                    entity.setOtherAuthority(null);
                    try {
                        mongoOperations.upsert(new Query(Criteria.where("_id").is(node.getId())), UpdateUtil.getUpdateValues(entity), entity.getClass());
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
            convertUtil.clearWorkDir();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return inputStream;
    }


    /**
     * 转为PNG格式
     */
    public void toPng(NodeEntity node, HttpServletResponse response) {
        try (ServletOutputStream out = response.getOutputStream()) {
            InputStream inputStream = getPreviewInputStream(node);
            if (inputStream != null) {
                String name = node.getName();
                name = name.substring(0, name.lastIndexOf(".")) + ".png";
                response.setHeader("Content-disposition",
                        String.format("attachment; filename=\"%s\"", new String(name.getBytes(StandardCharsets.UTF_8), "ISO8859-1")));
                PDFStreamUtil pdfUtil = new PDFStreamUtil();
                pdfUtil.pdf2Image(node.getName(), inputStream, out, 100);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 转为SVG格式
     */
    public void toSvg(NodeEntity node, HttpServletResponse response) {
        try (ServletOutputStream out = response.getOutputStream()) {
            InputStream inputStream = getPreviewInputStream(node);
            if (inputStream != null) {
                String name = node.getName();
                name = name.substring(0, name.lastIndexOf(".")) + ".svg";
                response.setHeader("Content-disposition",
                        String.format("attachment; filename=\"%s\"", new String(name.getBytes(StandardCharsets.UTF_8), "ISO8859-1")));
                SvgUtils.pdfToSvg(inputStream, out);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
